package switchviz.network.snmp;

import java.io.IOException;
import java.util.Date;
import java.util.List;

import javax.swing.JTextArea;
import javax.swing.SwingWorker;

import org.apache.log4j.Logger;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

import switchviz.network.netswitch.SwitchDataModel;

public class SnmpWalkerWorker extends SwingWorker<String, String>
{
	private static Logger logger = Logger.getLogger(SnmpWalkerWorker.class);
	private JTextArea _textArea;
	private String _oid;
	private boolean finished = false;
	private OID targetOID;
	private PDU requestPDU;
	private Snmp snmp;
	private CommunityTarget target;
	private SwitchDataModel switchdatamodel;
	private TransportMapping transport;
	
	public SnmpWalkerWorker(JTextArea textArea, String oid, SwitchDataModel switchdatamodel)
	{
		this._textArea = textArea;
		this._oid = oid;
		this.switchdatamodel = switchdatamodel;
	}
	
	private String nextOid() throws IOException
	{
		String line = null;
		VariableBinding vb = null;

		ResponseEvent responseEvent = snmp.send(requestPDU, target);

		PDU responsePDU = responseEvent.getResponse();
		if (responsePDU != null)
		{
			vb = responsePDU.get(0);
		}

		if (responsePDU == null)
		{
			line = "Response timed out!";
			finished = true;
		}
		else if (responsePDU.getErrorStatus() != 0)
		{
			line = "Response Error: " + responsePDU.getErrorStatusText();
			finished = true;
		}
		else if (vb.getOid() == null)
		{
			line = "Could not read the OID";
			finished = true;
		}
		else if (vb.getOid().size() < targetOID.size())
		{
			line = "New OID Size is smaller than Start OID: " + vb.getOid();
			finished = true;
		}
		else if (targetOID.leftMostCompare(targetOID.size(),
				vb.getOid()) != 0)
		{
			line = "New OID root: " + vb.getOid();
			finished = true;
		}
		else if (vb.getOid().compareTo(targetOID) <= 0)
		{
			line = "Variable received is not "+
					"lexicographic successor of requested "+
			"one:" + vb.toString() + " <= " + targetOID;
			finished = true;
		}
		else
		{
			// Dump response.
			line = vb.toString();

			// Set up the variable binding for the next entry.
			requestPDU.setRequestID(new Integer32(0));
			requestPDU.set(0, vb);
		}
		return line;
	}
	
	public void stop() throws IOException
	{
		snmp.close();
		publish("SNMP Walk stopped.");
		publish("End Time: " + new Date());
		finished = true;
	}
	
	@Override
	public String doInBackground() throws Exception 
	{
		String line = "";
		targetOID = new OID(_oid);
		
		requestPDU = new PDU();
		requestPDU.add(new VariableBinding(targetOID));
		requestPDU.setType(PDU.GETNEXT);

		target = new CommunityTarget(SnmpAdaptor.parseAddress(switchdatamodel.getAddress(), 
												 	 switchdatamodel.getPort()),
												 	 new OctetString(switchdatamodel.getCommunity()));
		target.setVersion(SnmpConstants.version2c);
		target.setTimeout(switchdatamodel.getTimeout());
		
		if(switchdatamodel.getMaxRetries() < 2)
			target.setRetries(2);
		else
			target.setRetries(switchdatamodel.getMaxRetries());
		
		try
		{
			transport = new DefaultUdpTransportMapping();
			snmp = new Snmp(transport);
			transport.listen();
			finished = false;
			String oldline = line + "_";
			publish("SnmpWalk started on " + switchdatamodel.getName() + " at " + 
					switchdatamodel.getAddress() + ":" + switchdatamodel.getPort());
			publish("Start Time: " + new Date());
			while (!finished)
			{
				line = nextOid();
				if(line.equals(oldline))
				{
					finished = true;
				}
				else
				{
					oldline = line;
					publish(line);
				}
			}
			publish("SnmpWalk finished!");
			publish("End Time: " + new Date());
			snmp.close();
		}
		catch (IOException e)
		{
			logger.error("IOException: " + e);
		}
		
		return line;
	}
	
	protected void process(List<String> line)
	{
		for(String l : line)
			_textArea.append(l + "\n");
	}
}
